package table

import (
	"container/list"
	"lol.com/feb214-game/nest.git/log"
	"lol.com/feb214-game/nest.git/tools/num"
	"lol.com/feb214-game/nest.git/tools/sample"
	"lol.com/feb214-game/nest.git/tools/tz"
	"math/rand"
	"sync"
	"sync/atomic"
	"talent.com/cube/common/cache"
	"talent.com/cube/common/cxt"
	"talent.com/cube/common/msg"
	"talent.com/cube/conf"
	"talent.com/cube/conf/g"
)

//每轮开始时进出机器人
func updateRobots(tc *cxt.TableContext) *sync.WaitGroup {
	robotConf := conf.RobotConfig(tc.GameType)
	if robotConf.ActiveCount[tc.Kind] == 0 {
		//关闭AI，移除所有机器人
		tc.Players.Map.Range(func(key, value interface{}) bool {
			player := value.(*cxt.UserContext)
			if player.IsRobot() {
				atomic.StoreInt32(&player.Disconnect, 1)
			}
			return true
		})
		return nil
	}

	activeCount := tc.Players.Length() - int(atomic.LoadInt32(&tc.UserCount))
	maxCount := robotConf.CurrentMaxActiveCount(tc.Kind)
	overCount := tc.Players.Length() - g.TableMaxUserCount

	//activeCount:活跃机器人数量
	//maxCount：根据配置，设置的最多玩家数量。基础活跃个数100，比例随时间变化
	//overCount：超出最大容纳人数的数量，无论机器人或者人。
	// 需要机器人的数量，在房间一直保持一定比例
	log.Info("activeCount: %v maxCount: %v overCount: %v", activeCount, maxCount, overCount)
	//人数过多，随机移除.
	overCount2 := activeCount - g.MinRobotCount
	if activeCount > maxCount || (overCount > 0 && overCount2 > 0) {
		if overCount > 0 {
			overCount = num.MinInt(activeCount-g.MinRobotCount, overCount)
			if overCount < 0 {
				overCount = 0
			}
		}
		toRemove := num.MaxInt(activeCount-maxCount, overCount)
		log.Info("removeNum: %v", toRemove)
		allRobots := make([]uint64, 0, activeCount)
		tc.Players.Map.Range(func(key, value interface{}) bool {
			userId := key.(uint64)
			user := value.(*cxt.UserContext)
			if user.IsRobot() {
				allRobots = append(allRobots, userId)
			}
			return true
		})
		sample.ShuffleUint64(allRobots)
		if toRemove > len(allRobots) {
			toRemove = len(allRobots)
		}
		//这时候新一局还没开始，直接移除
		for _, uid := range allRobots[:toRemove] {
			atomic.StoreInt32(&tc.Players.GetPlayer(uid).Disconnect, 1)
			removeTableUser(tc, uid)
		}
		return nil
	}

	leftMax := g.TableMaxUserCount - atomic.LoadInt32(&tc.UserCount)

	//leftMax += g.MinRobotCount // 即使房间满了，也要加上默认的机器人数量
	// 房间最大容量下的机器人数量
	if leftMax < 5 { //真实用户太多，不再添加机器人
		//return nil
		if activeCount >= g.MinRobotCount {
			leftMax = 0
		} else {
			leftMax = g.MinRobotCount - int32(activeCount)
		}
	}
	//if leftMax >= int32(maxCount) {
	//	return nil
	//}
	//人数不足，填充新的机器人
	var (
		level       int
		ust         *msg.UserST
		creditRange []int64
		credit      int64

		robotList = cache.GetRobotList(tc.GameType, tc.ID)
		gameLabel = g.GetCodeLabel(tc.GameType)
	)
	if robotList == nil {
		log.Error("can't get robot for %v, room %v, check config!!!", gameLabel, tc.Kind)
		return nil
	}
	toSync := make([]*cxt.UserContext, 0)
	log.Info("add robot maxCount-activeCount: %v, leftMax: %v", maxCount-activeCount, leftMax)
	for toAdd := num.MinInt(maxCount-activeCount, int(leftMax)); toAdd > 0; toAdd-- {
		level = robotConf.GetRandomLevel()
		ust = robotList.Dequeue(level)
		if ust == nil {
			log.Warn("[%v]level %v robot not enough, please add more!!!", gameLabel, level)
			continue
		}
		//金额调整，注意单位
		creditRange = robotConf.LevelConfig[level].InitCredit
		ust.BetInfo = make(map[int32]*msg.ChipInfo)
		uc := &cxt.UserContext{
			UserST:     ust,
			GameType:   tc.GameType,
			RoomKind:   tc.Kind,
			TableID:    tc.ID,
			RobotLevel: level,
		}
		if credit < creditRange[0] || credit > creditRange[1] {
			ust.Credit = sample.RandInt64(creditRange[0], creditRange[1]) * g.CoinRate
			toSync = append(toSync, uc)
			continue
		}
		//直接加进来
		tc.Players.SetPlayers(ust.ID, uc)
		log.Info("[%v]add level %v robot %v to room %v table %v, credit:%v",
			gameLabel, level, ust.ID, tc.Kind, tc.ID, ust.Credit)
	}
	//初始化金额以后再加进来（本局不会下注）
	if len(toSync) > 0 {
		finish := &sync.WaitGroup{}
		finish.Add(1)
		tc.Skeleton.Go(func() {
			for _, uc := range toSync {
				if uc.Disconnect == 0 {
					tc.Players.SetPlayers(uc.ID, uc)
					log.Info("[%v]add level %v robot %v to room %v table %v after init credit to %v",
						gameLabel, uc.RobotLevel, uc.ID, tc.Kind, tc.ID, uc.Credit)
				} else {
					robotList.Enqueue(uc.UserST, uc.RobotLevel, 10) //理论上这时候数据库有问题，稍微延迟一下
				}
			}
			go finish.Done()
		}, nil)
		return finish
	}
	return nil
}

//随机机器人下注计划
func planRobotBet(tc *cxt.TableContext) {
	tc.TermPlan = list.New()
	var (
		chipIndex  int
		count      int
		chipAmount int64

		robotConf   = conf.RobotConfig(tc.GameType)
		chipAmounts = conf.RoomConfig(tc.GameType, tc.Kind).Chips
		betIndex    = int32(-1)
		plans       = make([]*msg.BetReq, 0)
		betRet      = make(map[int32]int)
	)

	tc.Players.Map.Range(func(key, value interface{}) bool {
		player := value.(*cxt.UserContext)
		//跳过真实用户和已标记离线的用户
		if !player.IsRobot() || atomic.LoadInt32(&player.Disconnect) == 1 {
			return true
		}
		//随机筹码索引，每局筹码不变
		chipIndex = robotConf.RandChipIndexByCredit(player.Credit)
		if chipIndex < 0 {
			return true
		}
		//根据筹码索引随机下注次数
		count = robotConf.RandBetCountByChip(chipIndex)

		chipAmount = chipAmounts[chipIndex]
		if int64(count)*chipAmount > player.Credit {
			count = int(player.Credit / chipAmount)
		}

		robotConf.ResetBetWeight()
		for ; count > 0; count-- {
			// TODO 重选未生效，似乎也没意义
			if betIndex == -1 {

				betIndex = robotConf.RandBetIndex()

			} else if rand.Float32() < robotConf.BetIndexReChooseRate {

				betIndex = robotConf.RandBetIndex()

			}

			betRet[betIndex]++
			plans = append(plans, &msg.BetReq{
				Timestamp:  int64(player.ID), // NOTE: trick, 避免新建数据结构，直接用时间戳存用户id
				TermNumber: tc.TermNumber,
				Info: map[int32]*msg.ChipInfo{
					betIndex: {
						Data: map[int64]int32{
							chipAmount: 1,
						},
					},
				},
			})
		}
		return true
	})
	//shuffle下注计划
	for i := range plans {
		j := rand.Intn(i + 1)
		plans[i], plans[j] = plans[j], plans[i]
	}
	for _, plan := range plans {
		tc.TermPlan.PushBack(plan)
	}
}

//每局结束后随机休眠机器人
func sleepRobot(tc *cxt.TableContext) {
	now := tz.GetNowTs() //活跃时间用秒计算
	var (
		quit      bool
		sleepSecs int
		robotConf = conf.RobotConfig(tc.GameType)
		credit    int64
	)
	tc.Players.Map.Range(func(key, value interface{}) bool {
		player := value.(*cxt.UserContext)
		//根据策略退出&休眠机器人
		if !player.IsRobot() || atomic.LoadInt32(&player.Disconnect) == 1 {
			return true
		}
		quit = false
		sleepSecs = 0
		//注意单位转换
		credit = player.Credit / g.CoinRate
		if robotConf.ActiveCount[tc.Kind] == 0 { //关闭机器人了
			quit = true
		} else if credit < robotConf.LevelConfig[player.RobotLevel].ResetEdge[0] ||
			credit > robotConf.LevelConfig[player.RobotLevel].ResetEdge[1] { //金钱范围不满足
			quit = true
			sleepSecs = sample.RandInt(robotConf.OverCreditSleepRange[0], robotConf.OverCreditSleepRange[1]) * 60
		} else if now-player.EnterAt >= (robotConf.WorkTimeLimit*60) && //活跃时间过长
			rand.Float32() < robotConf.OverWorkSleepRate {
			quit = true
			sleepSecs = sample.RandInt(robotConf.OverWorkSleepRange[0], robotConf.OverWorkSleepRange[1]) * 60
			player.EnterAt = 0 //重置累计活跃时间
		} else if rand.Float32() <= robotConf.TermQuitRate { //每局固定随机退出
			quit = true
			sleepSecs = sample.RandInt(robotConf.TermQuitSleepRange[0], robotConf.TermQuitSleepRange[1]) * 60
		}
		if quit {
			atomic.StoreInt32(&player.Disconnect, 1)
			player.SleepUntil = tz.GetNowTs() + int64(sleepSecs)
		}
		return true
	})
}
